#pragma once
#include <memory>
#include <functional>
#include <boost/signals2.hpp>
#include <boost/describe.hpp>
#include <SFML/Graphics/Drawable.hpp>
#include <nice2d/object.h>
#include <nice2d/serializer.h>

class NodeVisitor;

enum NodeRenderPolicy {
    NodeRenderPolicy_ChildrenFirst,
    NodeRenderPolicy_ParentFirst
};

BOOST_DESCRIBE_ENUM(NodeRenderPolicy, NodeRenderPolicy_ChildrenFirst, NodeRenderPolicy_ParentFirst)

class Node :
    public Object,
    public sf::Drawable,
    public Serializer
{
    friend class NodeData;
    DECLARE_OBJECT(Node)
public:
    Node();
    virtual ~Node();
public:
    void setPosition(float x, float y);
    void setPosition(const sf::Vector2f &position);
    sf::Vector2f getPosition()const;
    boost::signals2::signal<void(const sf::Vector2f &)> positionChanged;

    void move(float dx, float dy);
    void move(float distance);

    void setRotate(float angle);
    float getRotate()const;
    boost::signals2::signal<void(float angle)> rotateChanged;

    void setScale(float scale);
    void setScale(float scalex, float scaley);
    void setScale(const sf::Vector2f &scale);
    sf::Vector2f getScale()const;
    boost::signals2::signal<void(const sf::Vector2f &)> scaleChanged;

    void setOrigin(float x, float y);
    void setOrigin(const sf::Vector2f &origin);
    sf::Vector2f getOrigin()const;
    boost::signals2::signal<void(const sf::Vector2f &)> originChanged;

    sf::Transform getTransform()const;
public:
    void addChild(NodePointer child);
    void detachChild(NodePointer child);
    void detachChild(std::function<bool(NodePointer)> fn);
    void visitChild(std::function<void(NodePointer)> fn);
    void visitSibling(std::function<void(NodePointer)> fn);

    NodeWeakPointer getParent()const;
    void acceptNode(NodeVisitor* visitor);
public:
    virtual bool process(std::shared_ptr<Message> message)override;
    void update(float deltaTime)override;
    void draw(sf::RenderTarget &target, sf::RenderStates states) const override;

    void setVisible(bool visible);
    boost::signals2::signal<void(bool)> visibleChanged;
    bool isVisible()const;

    void setRenderChildren(bool render);
    bool isRenderChildren()const;

    void setRenderPolicy(NodeRenderPolicy order);
    NodeRenderPolicy getRenderOrder()const;
    void setRenderObject(bool render = true);
    bool isRenderObject()const;
public:
    virtual PTree serialize() override;
    virtual void deserialize(const PTree &node) override;
protected:
    virtual void onVisibleChanged();
    virtual void onDraw(sf::RenderTarget &target, sf::RenderStates states)const;
    virtual void onDrawNode(sf::RenderTarget &target, sf::RenderStates states) const;
    virtual void onUpdateNode(float deltaTime);

    virtual void onPositionChanged();
    virtual void onRotateChanged();
    virtual void onScaleChanged();
    virtual void onOriginChanged();
private:
    std::unique_ptr<class NodeData> data;
};

class NodeVisitor
{
public:
    virtual ~NodeVisitor();
public:
    virtual void visitNode(const NodePointer object);
};